package com.nulldev.util.concurrency.threadIt.v4.executors;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.ExecutorFeatureExtensions;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.ExtendedExecutor;
import com.nulldev.util.concurrency.threadIt.v4.utils.impls.TEConditions;

public class LightningFJ extends ForkJoinPool implements ExtendedExecutor {
	public static final int DEFAULT_AMOUNT = Arguments.hasArgument("--Lightning.legacyDefault") ? 128 : 64;

	private final int threadCount;

	/*
	 * TODO: - Add LightningFJWorkers - Optimizations
	 */

	public LightningFJ() {
		this(DEFAULT_AMOUNT);
	}

	public LightningFJ(final int threadCount) {
		super(threadCount, ForkJoinPool.defaultForkJoinWorkerThreadFactory, new UncaughtExceptionHandler() {

			@Override
			public void uncaughtException(Thread thread, Throwable throwable) {
				JVM.errorln(throwable);
			}

		}, true);
		this.threadCount = threadCount;
	}

	public int getThreadCount() {
		return this.threadCount;
	}

	@Override
	public int executor_getFreeSlots() {
		return MathUtil.clamp(1, this.threadCount, this.getThreadCount() - this.getRunningThreadCount());
	}

	@Override
	public boolean executorSupportsSlotCount() {
		return true;
	}

	private static final ExecutorFeatureExtensions[] SUPPORTED = new ExecutorFeatureExtensions[]
		{ ExecutorFeatureExtensions.EEX_core_executor_slots, ExecutorFeatureExtensions.EEX_query_slots_supported,
				ExecutorFeatureExtensions.EEX_core_submit_all_tasks, ExecutorFeatureExtensions.EEX_core_drain_task_queue,
				ExecutorFeatureExtensions.EEX_executor_native_batching, ExecutorFeatureExtensions.EEX_executor_await_idle,
				ExecutorFeatureExtensions.EEX_query_batching_supported, ExecutorFeatureExtensions.EEX_query_slots_supported, };

	@Override
	public ExecutorFeatureExtensions[] supported() {
		return SUPPORTED;
	}

	@Override
	public boolean supportsFeature(final ExecutorFeatureExtensions feature) {
		return ArrayUtils.contains(SUPPORTED, feature);
	}

	@Override
	public String toString() {
		return "LightningFJ@" + MathUtil.toHex(this.hashCode()) + "[threads=" + this.threadCount + ",active=" + this.getActiveThreadCount() + "]";
	}

	@Override
	public void awaitIdle() {
		final int limit = 8;
		int waitingInt = limit;
		final int s = (int) (35 / limit);
		while (this.hasQueuedSubmissions()) {
			try {
				if (waitingInt <= 0) {
					waitingInt = limit + 1;
				}
				Thread.sleep((long) Math.ceil(s / waitingInt));
				waitingInt -= 1;
			} catch (InterruptedException e) {
			}
		}
	}

	@Override
	public void executor_submitAll(final List<Runnable> r) {
		if (r == null || r.isEmpty())
			return;
		Runnable t;
//		final ForkJoinTask<?>[] tasks = new ForkJoinTask<?>[r.size()];
//		for (int i = 0; i < r.size(); i++) {
//			tasks[i] = ForkJoinTask.adapt(r.remove(0));
//		}
//		super.invoke(null);
//		ForkJoinTask.invokeAll(tasks);
		while ((t = r.remove(0)) != null && !r.isEmpty()) {
			super.execute(ForkJoinTask.adapt(t));
		}
	}

	@Override
	public void executor_drainTasks(final Queue<Runnable> async_queue, final int batchSize) {
		if (async_queue == null || async_queue.isEmpty())
			return;
		if (batchSize <= 0)
			return;
		for (int i = 0; i < batchSize; i++) {
			Runnable r = null;
			try {
				r = async_queue.poll();
			} catch (Exception e) {
				continue;
			} finally {
				if (r == null)
					continue;
				super.execute(ForkJoinTask.adapt(r));
			}
		}
	}

	@Override
	public void batch(final Runnable f) {
		TEConditions.validRunnable(f);
		super.execute(ForkJoinTask.adapt(f));
	}

	@Override
	public void batchAll(final Runnable[] _r) {
		if (_r == null || _r.length == 0)
			return;
		for (final Runnable r : _r)
			super.execute(ForkJoinTask.adapt(r));
	}

	/* EEX_query_batching_supported */

	@Override
	public boolean executorSupportsBatching() {
// FIXME: We are currently massive liars, is that a good idea?
		return true;
	}
}
